package com.zdh.strategy;

import com.alibaba.fastjson.JSON;
import com.zdh.strategy.kit.NumKit;
import com.zdh.strategy.kit.Tuple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.*;

public class GridFollowBidAskV0 {
    private static final Logger logger = LoggerFactory.getLogger(GridFollowBidAskV0.class);

    public static class Config {
        public BigDecimal followBidAskAdjustRate = BigDecimal.ZERO;
        public BigDecimal followAskAskAdjustRate = BigDecimal.ZERO;
        public String followSymbol;
        public String orderSymbol;
        public int depthSize;
        public List<Tuple.T3<BigDecimal, BigDecimal, BigDecimal>> priceSpan;
        public BigDecimal assetPercentMin;
        public BigDecimal assetPercentMax;
        public BigDecimal assetBaseAmountUseRate = new BigDecimal("0.3");
        public BigDecimal assetQuoteAmountUseRate = new BigDecimal("0.3");
        public BigDecimal priceRangeRate;
        public BigDecimal qtyRangeRate;
        public Map<String, String> tradeConfig;
        public Map<String, String> marketDataConfig;
        public Boolean followBinance = true;
    }

    public static void main(String[] args) {
        Config config = new Config();
        config.followSymbol = "BCHUSDT";
        config.orderSymbol = "BCHUSDT";
        config.depthSize = 4;
        config.priceSpan = new ArrayList<Tuple.T3<BigDecimal, BigDecimal, BigDecimal>>() {{
            add(Tuple.create(new BigDecimal("0.0001"), new BigDecimal("0.005"), new BigDecimal("0.0001")));
        }};
        config.assetPercentMin = new BigDecimal("2");
        config.assetPercentMax = new BigDecimal("20");
        config.priceRangeRate = new BigDecimal("0.1");
        config.qtyRangeRate = new BigDecimal("0.1");
        System.out.println(JSON.toJSONString(GridFollowBidAskV0.generateRandomOB(new BigDecimal("8000"), new BigDecimal("8004"), new BigDecimal("0.02"), config)));
    }

    // priceSpan BigDecimal min, BigDecimal max, BigDecimal step
    // return bids asks
    static Tuple.T2<List<Tuple.T2<BigDecimal, BigDecimal>>, List<Tuple.T2<BigDecimal, BigDecimal>>>
            generateRandomOB(
                    BigDecimal bid1, BigDecimal ask1, BigDecimal priceTickSize, Config config) {
        final int size = config.depthSize;
        final List<Tuple.T3<BigDecimal, BigDecimal, BigDecimal>> priceSpan = config.priceSpan;
        final BigDecimal assetRatioMin = config.assetPercentMin;
        final BigDecimal assetRatioMax = config.assetPercentMax;

        // 生成价格
        TreeMap<BigDecimal, BigDecimal> bids = new TreeMap<>(Comparator.reverseOrder());
        bids.put(bid1.multiply(BigDecimal.ONE.subtract(config.followBidAskAdjustRate)), null);
        TreeMap<BigDecimal, BigDecimal> asks = new TreeMap<>();
        asks.put(ask1.multiply(BigDecimal.ONE.add(config.followAskAskAdjustRate)), null);
        for (int i = 0; i < size; i++) {
            final Tuple.T3<BigDecimal, BigDecimal, BigDecimal> span;
            if (i < priceSpan.size()) {
                span = priceSpan.get(i);
            } else {
                span = priceSpan.get(priceSpan.size() - 1);
            }

            BigDecimal lastBid = bids.lastKey();
            BigDecimal lastAsk = asks.lastKey();

            BigDecimal r0 = NumKit.randomNumber(span._0, span._1, span._2);
            bids.put(
                    NumKit.limitStride(
                            lastBid.multiply(BigDecimal.ONE.subtract(r0)), priceTickSize),
                    null);
            BigDecimal r1 = NumKit.randomNumber(span._0, span._1, span._2);
            asks.put(
                    NumKit.limitStride(lastAsk.multiply(BigDecimal.ONE.add(r1)), priceTickSize),
                    null);
        }

        // 生成资产占比
        BigDecimal step = BigDecimal.TEN.movePointLeft(11);
        BigDecimal amount = new BigDecimal(100);
        List<BigDecimal> bidAssetRatio =
                NumKit.splitAmount(amount, bids.size(), assetRatioMin, assetRatioMax, step);
        List<BigDecimal> askAssetRatio =
                NumKit.splitAmount(amount, asks.size(), assetRatioMin, assetRatioMax, step);

        ArrayList<BigDecimal> cp_BidAssetRatio = new ArrayList<>(bidAssetRatio);
        ArrayList<BigDecimal> cp_AskAssetRatio = new ArrayList<>(askAssetRatio);
        Collections.sort(cp_BidAssetRatio);
        Collections.sort(cp_AskAssetRatio);
        int v = (int) (size * 0.1);
        ArrayList<Tuple.T2<BigDecimal, BigDecimal>> cp_bid = new ArrayList<>();
        ArrayList<Tuple.T2<BigDecimal, BigDecimal>> cp_ask = new ArrayList<>();
        int i = 0;
        //noinspection DuplicatedCode
        for (BigDecimal k : bids.keySet()) {
            if (i <= v) {
                cp_bid.add(Tuple.create(k, cp_BidAssetRatio.get(i).movePointLeft(2)));
            } else {
                cp_bid.add(Tuple.create(k, bidAssetRatio.get(i).movePointLeft(2)));
            }
            i = i + 1;
        }
        i = 0;
        //noinspection DuplicatedCode
        for (BigDecimal k : asks.keySet()) {
            if (i <= v) {
                cp_ask.add(Tuple.create(k, cp_AskAssetRatio.get(i).movePointLeft(2)));
            } else {
                cp_ask.add(Tuple.create(k, askAssetRatio.get(i).movePointLeft(2)));
            }
            i = i + 1;
        }
        logger.info(
                "generateRandomOB followSymbol:{},orderSymbol:{},bid0:{},ask0:{},bidSize:{},askSize:{}",
                config.followSymbol,
                config.orderSymbol,
                bid1,
                ask1,
                bids.size(),
                asks.size());
        return Tuple.create(cp_bid, cp_ask);
    }

    // return : 下单的, 撤单的
    //   String orderId = order._0;
    //   OrderSide orderSide = order._1;
    //   BigDecimal limitPrice = order._2;
    //   BigDecimal orderQty = order._3;
    static Tuple.T2<List<Tuple.T3<OrderSide, BigDecimal, BigDecimal>>, List<String>>
            orderSyncAction(
                    List<Tuple.T4<String, OrderSide, BigDecimal, BigDecimal>> openOrders,
                    BigDecimal bid1,
                    BigDecimal ask1,
                    BigDecimal priceTickSize,
                    BigDecimal qtyTickSize,
                    BigDecimal assetBaseAmount,
                    BigDecimal assetQuoteAmount,
                    Config config) {
        final BigDecimal priceRangeRate = config.priceRangeRate;
        final BigDecimal qtyRangeRate = config.qtyRangeRate;

        // 方向，价格，数量
        List<Tuple.T3<OrderSide, BigDecimal, BigDecimal>> insertOrders = new ArrayList<>();
        // 订单Id
        List<String> cancelOrders = new ArrayList<>();
        //
        Tuple.T2<List<Tuple.T2<BigDecimal, BigDecimal>>, List<Tuple.T2<BigDecimal, BigDecimal>>>
                randomOB = generateRandomOB(bid1, ask1, priceTickSize, config);
        List<Tuple.T2<BigDecimal, BigDecimal>> bids = randomOB._0;
        List<Tuple.T2<BigDecimal, BigDecimal>> asks = randomOB._1;

        // 对订单进行排序
        TreeMap<BigDecimal, Tuple.T4<String, OrderSide, BigDecimal, BigDecimal>> order_bids =
                new TreeMap<>(Comparator.reverseOrder());
        TreeMap<BigDecimal, Tuple.T4<String, OrderSide, BigDecimal, BigDecimal>> order_asks =
                new TreeMap<>();
        for (Tuple.T4<String, OrderSide, BigDecimal, BigDecimal> order : openOrders) {
            if (order._1.equals(OrderSide.BUY)) {
                order_bids.put(order._2, order);
            } else {
                order_asks.put(order._2, order);
            }
        }
        ArrayList<Tuple.T4<String, OrderSide, BigDecimal, BigDecimal>> orders_cp =
                new ArrayList<>();
        orders_cp.addAll(order_bids.values());
        orders_cp.addAll(order_asks.values());

        // 对订单调整
        HashSet<BigDecimal> ownPrice = new HashSet<>();
        for (Tuple.T4<String, OrderSide, BigDecimal, BigDecimal> order : orders_cp) {
            String orderId = order._0;
            OrderSide orderSide = order._1;
            BigDecimal limitPrice = order._2;
            BigDecimal orderQty = order._3;

            boolean keep = false;
            if (orderSide.equals(OrderSide.BUY)) {
                int maxTrimming = config.depthSize / 2;
                for (Tuple.T2<BigDecimal, BigDecimal> p : bids) {
                    BigDecimal price = p._0;
                    BigDecimal radio = p._1;
                    BigDecimal orderAmount = limitPrice.multiply(orderQty);
                    BigDecimal orderRadio = NumKit.divide(orderAmount, assetQuoteAmount);
                    if (
                    //                            (maxTrimming <= 0) ||
                    (NumKit.le(
                                    NumKit.divide(limitPrice.subtract(price), price).abs(),
                                    priceRangeRate)
                            && NumKit.le(
                                    NumKit.divide(orderRadio.subtract(radio), radio).abs(),
                                    qtyRangeRate))) {
                        ownPrice.add(price);
                        keep = true;
                    } else {
                        maxTrimming--;
                    }
                }
            } else if (orderSide.equals(OrderSide.SELL)) {
                int maxTrimming = config.depthSize / 2;
                for (Tuple.T2<BigDecimal, BigDecimal> p : asks) {
                    BigDecimal price = p._0;
                    BigDecimal radio = p._1;
                    if (
                    //                            (maxTrimming <= 0) ||
                    (NumKit.le(
                                    NumKit.divide(limitPrice.subtract(price), price).abs(),
                                    priceRangeRate)
                            && NumKit.le(
                                    NumKit.divide(
                                                    NumKit.divide(orderQty, assetBaseAmount)
                                                            .subtract(radio),
                                                    radio)
                                            .abs(),
                                    qtyRangeRate))) {
                        ownPrice.add(price);
                        keep = true;
                    } else {
                        maxTrimming--;
                    }
                }
            }
            if (!keep) {
                cancelOrders.add(orderId);
            }
        }

        for (Tuple.T2<BigDecimal, BigDecimal> e : bids) {
            BigDecimal price = e._0;
            BigDecimal r = e._1;
            if (ownPrice.contains(price)) {
                continue;
            }
            BigDecimal qty =
                    NumKit.limitStride(
                            NumKit.divide(assetQuoteAmount.multiply(r), price), qtyTickSize);
            price = NumKit.limitStride(price, priceTickSize);
            if (NumKit.gt(qty, BigDecimal.ZERO) && NumKit.gt(price, BigDecimal.ZERO)) {
                insertOrders.add(Tuple.create(OrderSide.BUY, price, qty));
            }
        }

        for (Tuple.T2<BigDecimal, BigDecimal> e : asks) {
            BigDecimal price = e._0;
            BigDecimal r = e._1;
            if (ownPrice.contains(price)) {
                continue;
            }
            BigDecimal qty = NumKit.limitStride(assetBaseAmount.multiply(r), qtyTickSize);
            price = NumKit.limitStride(price, priceTickSize);
            if (NumKit.gt(qty, BigDecimal.ZERO) && NumKit.gt(price, BigDecimal.ZERO)) {
                insertOrders.add(Tuple.create(OrderSide.SELL, price, qty));
            }
        }

        return Tuple.create(insertOrders, cancelOrders);
    }
}
